/*!
 * Loader v1.0.0 of jRaiser javascript library
 * http://code.google.com/p/jraiser/
 *
 * Author: Heero.Luo
 * Licensed under MIT license
 *
 * Date: Wed Jul 04 2012 16:21:39 GMT+0800
 */
!function(window, undefined) { 'use strict';

var jr = window.jRaiser = { };

// 配置
var config = {
	root : './',
	charset : 'utf-8',
	debug : true
};

var isOpera = window.opera != null && window.opera.toString() === '[object Opera]',
	useInteractive = window.attachEvent && !isOpera;


// 去掉代码中的注释
// from http://lifesinger.github.com/lab/2011/remove-comments-safely/
function removeComments(code) {
	return code
		.replace(/(?:^|\n|\r)\s*\/\*[\s\S]*?\*\/\s*(?:\r|\n|$)/g, '\n')
		.replace(/(?:^|\n|\r)\s*\/\/.*(?:\r|\n|$)/g, '\n');
}

// 根据值去重复
function uniqueByVal(arr) {
	var ret = [ ], temp = { }, elt;
	for (var i = 0; i < arr.length; i++) {
		elt = arr[i];
		if (!temp[elt]) {
			temp[elt] = true;
			ret.push(elt);
		}
	}
	
	return ret;
}

// 分析出依赖的元素 from seajs
function parseDependencies(code) {
	var pattern = /(?:^|[^.])\brequire\s*\(\s*(["'])([^"'\s\)]+)\1\s*\)/g;
	var ret = [ ], match;

	code = removeComments(code);
	while ( match = pattern.exec(code) ) {
		if (match[2]) {
			ret.push(match[2]);
		}
	}

	return uniqueByVal(ret);
}

// 检测是否空对象
function isEmptyObject(obj) {
	for (var p in obj) { return false; }
	return true;
}

// 把ID转换成URL
function idToURL(id) {
	// 绝对路径，直接返回
	if ( /^(?:https?|file):/.test(id) ) { return id; }

	var re_extName = /\.[^.\/\\#]+$/;

	// 添加默认扩展名
	if ( id.indexOf('?') === -1 && !re_extName.test(id) ) {
		id += '.js';
	}

	// debug模式时，添加'-debug'到文件名
	if (config.debug) {
		id = id.replace(/\.[^.\/\\#?]+(?:\?.*)?$/, function($0) {
			return '-debug' + $0;
		});
	}

	var path = /^\//.test(id) ? id :
		( (config.root || '') + id ).replace(/([^:])\/{2,}/g, function($0, $1) {
			return $1 + '/';
		});

	// IE 6/7下只有通过innerHTML写入的a元素才能获得绝对路径
	var div = document.createElement('div');
	div.innerHTML = '<a href="' + path + '"></a>';
	id = div.firstChild.href;
	div = null;

	return id;
}


/**
 * 脚本读取器
 */
var scriptLoader = {
	// script的父节点
	_scriptParent : document.getElementsByTagName('head')[0],
	
	/**
	 * 标注加载中（1）或已加载（2）的url
	 */
	_loaded : { },
	
	/**
	 * 获取当前运行的脚本所在的script标签
	 */
	getExecutingScript : function() {
		if (this._currentlyAddingScript) {
			return this._currentlyAddingScript;
		}
		
		var executingScript = this._executingScript;
		if (executingScript && executingScript.readyState === 'interactive') {
			return executingScript;
		}
	
		var scripts = this._scriptParent.getElementsByTagName('script'), script;
		for (var i = 0; i < scripts.length; i++) {
			script = scripts[i];
			if (script.readyState === 'interactive') {
				return (this._executingScript = script);
			}
		}
	},
	
	/**
	 * 加载script文件
	 * @param {String} 文件URL
	 * @param {Function} 加载完成后执行的回调
	 */
	load : function(src, onload) {
		var self = this, script = document.createElement('script');
		script.src = src;
		src = script.src;

		var state = self._loaded[src];

		if (state) {
			// 已加载，直接执行回调
			if (state === 2 && onload) { onload(script); }
			script = null;
		} else {
			self._loaded[src] = 1;	
			script.charset = config.charset;
			script.async = 'async';
			script.onload = script.onreadystatechange = script.onerror = function() {			
				if (!script.readyState ||
					'loaded' === script.readyState || 'complete' === script.readyState
				) {
					self._loaded[src] = 2;
					self._executingScript = null;
				
					script.onload = script.onreadystatechange = script.onerror = null;
					onload && onload(script);
					script.parentNode.removeChild(script);
					script = null;
				}
			};
			
			this._currentlyAddingScript = script;
			self._scriptParent.insertBefore(script, self._scriptParent.firstChild);
			this._currentlyAddingScript = null;
		}
	}
};


/**
 * 模块依赖链
 */
var dependentChain = {
	/**
	 * 记录某个模块被哪些模块依赖
	 */
	_chain : { },

	/**
	 * 添加依赖记录
	 * @param {String} 主模块id
	 * @param {String} 被依赖模块id
	 */
	add : function(moduleId, dependencyId) {
		var chain = this._chain[dependencyId] = this._chain[dependencyId] || [ ];
		chain.push(moduleId);
	},
	
	/**
	 * 获取依赖于指定模块的模块记录
	 * @param {String} 被依赖模块id
	 * @return {Array} 依赖于指定模块的模块记录
	 */
	get : function(dependencyId) { return this._chain[dependencyId]; },
	
	/**
	 * 清除依赖于某个模块的模块记录（当被依赖模块就绪时，即可清除）
	 * @param {String} 被依赖模块id
	 */
	clear : function(dependencyId) { delete this._chain[dependencyId]; }
};


/**
 * 模块类
 * @param {String} 模块id
 * @param {Function} 构造函数
 * @param {Array} 依赖的模块
 */
function Module(id, factory, dependencies) {
	this._factory = factory;
	
	// 处理依赖模块
	if (dependencies != null && dependencies.length) {
		this._dependencies = [ ];
		for (var i = dependencies.length - 1; i >= 0; i--) {
			this._dependencies[i] = idToURL(dependencies[i]);
		}
	}

	if (id) {
		this.setId(id);
	} else {
		Module.anonymous = this;
	}
}

var moduleProto = Module.prototype;
/**
 * 设置模块id（如果模块id已存在，则抛出异常）
 * @param {String} 模块id
 */
moduleProto.setId = function(id) {
	if (this._id) { throw new Error('module id cannot be changed'); }

	this._id = id;
	
	var dependencies = this._dependencies;
	if (dependencies) {
		this._readyStates = { };

		for (var i = dependencies.length - 1, dependency, dependencyId; i >= 0; i--) {
			dependencyId = dependencies[i];
			dependency = Module.all[dependencyId];
			
			// 模块不存在，需要加载
			if ( !dependency || !dependency.isReady() ) {		
				// 记录到依赖链
				dependentChain.add(id, dependencyId);
				// 记录此模块尚未就绪
				this._readyStates[dependencyId] = true;
				
				Module.load(dependencyId);
			}
		}
		
		if ( isEmptyObject(this._readyStates) ) {
			delete this._readyStates;
		}
	}

	Module.all[id] = this;
	
	this._checkReady();
};
/**
 * 检查此模块是否就绪，如果是，则根据依赖链通知被依赖模块
 */
moduleProto._checkReady = function() {
	if ( this.isReady() ) {
		if ( this.isTask() ) {
			setTimeout(function() {
				taskManager.tryExecute();
			}, 0);
		} else {
			var moduleIds = dependentChain.get(this._id);
			if (moduleIds) {
				// 逐一通知被依赖模块
				for (var i = moduleIds.length - 1, module; i >= 0; i--) {
					module = Module.all[ moduleIds[i] ];
					if (module) {
						module.notifyReady(this._id);
					}
				}

				dependentChain.clear(this._id);
			}
		}
	}
};
/**
 * 获取模块id
 * @return {String} 模块id
 */
moduleProto.id = function() { return this._id; };
/**
 * 获取此模块是否任务模块
 * @return {Boolean} 此模块是否任务模块
 */
moduleProto.isTask = function() { return /^#/.test(this._id); };
/**
 * 获取模块是否就绪
 * @return {Boolean} 模块是否就绪
 */
moduleProto.isReady = function() { return this._readyStates === undefined; }
/**
 * 通知当前模块，其某个依赖模块已经就绪
 * @param {String} 依赖模块id
 */
moduleProto.notifyReady = function(dependencyId) {
	var readyStates = this._readyStates;
	if (readyStates) {
		delete readyStates[dependencyId];
		if ( isEmptyObject(readyStates) ) {
			delete this._readyStates;
		}
	}

	this._checkReady();
};
/**
 * 执行模块构造函数，并返回其提供的接口
 * @param {Object} 模块提供的接口
 */
moduleProto.exports = function() {
	var module = this._executedModule;
	if (module) { return module.exports; }
	
	module = {
		id : this._id,
		exports : { }
	};
	this._factory.call(window, Module.require, module.exports, module);
	
	this._executedModule = module;
	
	return module.exports;
};
/**
 * 执行任务模块
 */
moduleProto.execute = function() {
	var modules = [ ], dependencies = this._dependencies;
	if (dependencies) {
		for (var i = dependencies.length - 1, module; i >= 0; i--) {
			module = Module.all[ dependencies[i] ];
			if (module) {
				modules[i] = module.exports();
			} else {
				throw new Error('module ' + dependencies[i] + ' is not loaded');
			}
		}
	}
	
	this._factory.apply(window, modules);
};
/**
 * 模块构造函数中的require函数
 */
Module.require = function(id) {
	var module = Module.all[idToURL(id)];
	if (module) {
		return module.exports();
	} else {
		throw new Error('module ' + id + ' is not loaded');
	}
};
/**
 * 加载模块
 * @param {String} 模块id
 */
Module.load = function(id) {
	scriptLoader.load(id, function(node) {
		id = node.src;
		
		if (Module.all[id]) { return; }

		if (!useInteractive && Module.anonymous) {
			Module.anonymous.setId(id);
			delete Module.anonymous;
		}

		if (!Module.all[id]) {
			throw new Error('module ' + id + ' does not exist');
		}
	});
};
/**
 * 记录所有模块的字典
 */
Module.all = { };


/**
 * 任务管理器
 */
var taskManager = {
	/**
	 * 自动编号
	 */
	_autoId : 0,

	/**
	 * 任务队列
	 */
	_queue : [ ],
	
	/**
	 * 添加任务
	 * @param {Function} 任务执行函数
	 */
	add : function(task) { this._queue.push(task); },
	
	/**
	 * 尝试执行任务
	 */
	tryExecute : function() {
		var task;
		while (task = this._queue[0]) {
			if ( task.isReady() ) {
				this._queue.shift();
				delete Module.all[task.id()];
				task.execute();
			} else {
				break;
			}
		}
	},
	
	/**
	 * 获取下一个任务模块的id
	 */
	nextId : function() { return '#' + ++this._autoId; }
};


/**
 * 声明模块（自动分析依赖模块）
 * @param {Function} 构造函数
 */
/**
 * 声明模块
 * @param {Array} 依赖的模块
 * @param {Function} 构造函数
 */
/**
 * 声明模块
 * @param {String} 模块Id
 * @param {Array} 依赖的模块
 * @param {Function} 构造函数
 */
window.define = function() {
	var id, dependencies, factory, args = arguments;
	
	// 重载
	switch (args.length) {
		case 1:
			factory = args[0];
			dependencies = parseDependencies( factory.toString() );
			break;
			
		case 2:
			dependencies = args[0];
			factory = args[1];
			break;
			
		case 3:
			id = args[0];
			dependencies = args[1];
			factory = args[2];
			break;
	}

	if (!id && useInteractive) {
		id = scriptLoader.getExecutingScript().src;
	}

	new Module(id, factory, dependencies);
};

/**
 * 使用指定模块执行操作
 * @param {Array} 模块id
 * @param {Function} 回调函数
 */
jr.use = function(ids, callback) {
	if (typeof ids === 'string') { ids = [ids]; }
	taskManager.add( new Module(taskManager.nextId(), callback, ids) );
};

/**
 * 更改配置
 * @param {Object} 新配置
 */
jr.config = function(newConfig) {
	for (var c in newConfig) {
		config[c] = newConfig[c];
	}
};


// 加载data-main属性指定的js
var scripts = document.getElementsByTagName('script'), mainJs;
for (var s = scripts.length - 1; s >= 0; s--) {
	if ( /jraiser/i.test(scripts[s].src) ) {
		mainJs = scripts[s].getAttribute('data-main');
		if (mainJs) {
			scriptLoader.load(mainJs);
			break;
		}
	}
}
scripts = null;

}(window);